#include <iosfwd>
#include <sstream>
#include "CodeInstruction.hpp"

using namespace std;

CodeInstruction::CodeInstruction() {}

CodeInstruction::CodeInstruction(const CodeInstruction& codInst) {}

CodeInstruction::~CodeInstruction() {}

string CodeInstruction::Deb()
{
    string retour = "nop" ;
    return retour;
}


string opMath(const TableId& ti, Operande& resultat, Operande& opG,
		Operande& opD, const string& op)
{
 	string retour;
    ostringstream outG;
    ostringstream outD;

    retour = ti.getId(resultat.getNum()) + " = ";
    if(opG.EstCalculee())
    {
        if(opG.getValeur()->getT()->getClass() == "integer")
            outG << opG.getValeur()->getValeurInteger();
        else if(opG.getValeur()->getT()->getClass() == "real")
            outG << opG.getValeur()->getValeurFloat();
        else {
			// Erreur ?
		}

		retour += outG.str();
 		retour += op;
	}
	else {
        retour += ti.getId(opG.getNum());
		retour += op;
    }

    if(opD.EstCalculee()){
        if(opD.getValeur()->getT()->getClass() == "integer")
            outD << opD.getValeur()->getValeurInteger();
        else if(opD.getValeur()->getT()->getClass() == "real")
            outD << opD.getValeur()->getValeurFloat();
        else {
			// Erreur ?
		}

        retour += outD.str();
    }
    else {
        retour += ti.getId(opD.getNum());
    }

    return retour;
}


string opMathStr(const TableId& ti, Operande& resultat, Operande& opG,
		Operande& opD, const string& op)
{
 	string retour;
    ostringstream outG;
    ostringstream outD;

    retour = ti.getId(resultat.getNum()) + " = ";
    if(opG.EstCalculee())
    {
        if(opG.getValeur()->getT()->getClass() == "integer")
            outG << opG.getValeur()->getValeurInteger();
        else if(opG.getValeur()->getT()->getClass() == "real")
            outG << opG.getValeur()->getValeurFloat();
        else if(opG.getValeur()->getT()->getClass() == "string")
            outG << opG.getValeur()->getValeurString();
		else {
			// Erreur ?
		}

		retour += outG.str();
 		retour += op;
	}
	else {
        retour += ti.getId(opG.getNum());
		retour += op;
    }

    if(opD.EstCalculee()){
        if(opD.getValeur()->getT()->getClass() == "integer")
            outD << opD.getValeur()->getValeurInteger();
        else if(opD.getValeur()->getT()->getClass() == "real")
            outD << opD.getValeur()->getValeurFloat();
        else if(opD.getValeur()->getT()->getClass() == "string")
            outD << opD.getValeur()->getValeurString();
		else {
			// Erreur ?
		}

        retour += outD.str();
    }
    else {
        retour += ti.getId(opD.getNum());
    }

    return retour;
}

string opBool(const TableId &ti, Operande &res, Operande &opG, Operande &opD,
		const string &op)
{
	string retour;
    ostringstream out1;
    ostringstream out2;

    retour = ti.getId(res.getNum()) + " = ";
    if(opG.EstCalculee())
    {
        if(opG.getValeur()->getT()->getClass() == "boolean")
            out1 << opG.getValeur()->getValeurBool();
		else {
			// Erreur ?
		}

        retour += out1.str();
		retour += op;
    }
    else
	{
        retour += ti.getId(opG.getNum());
		retour += op;
    }
    if(opD.EstCalculee())
	{
        if(opD.getValeur()->getT()->getClass() == "boolean")
            out2 << opD.getValeur()->getValeurBool();
		else {
			// Erreur ?
		}

        retour += out2.str();
    }
    else
	{
        retour += ti.getId(opD.getNum());
    }

    return retour;
}

string CodeInstruction::Add(TableId &ti, Operande& resultat,
		Operande& arg1, Operande& arg2)
{
	return opMathStr(ti, resultat, arg1, arg2, "+");
}

string CodeInstruction::Add(TableId &ti, Operande& arg1, Operande& arg2)
{
	return "TODO";
}

string CodeInstruction::Sub(TableId &ti, Operande& resultat,
		Operande& arg1, Operande& arg2)
{
	return opMath(ti, resultat, arg1, arg2, "-");
}

string CodeInstruction::Sub(Operande& arg1, Operande& arg2)
{
	return "TODO";
}

string stringCheck(const string &str, Operande &operande)
{
	ostringstream out;

	if(str.empty()) {
		//cout << "Debug empty: " << operande.getValeur()->toString() << endl;
        if(operande.getValeur()->getT()->getClass()
				.compare("boolean") == 0)
            out << operande.getValeur()->getValeurBool();
		else if(operande.getValeur()->getT()->getClass()
				.compare("integer") == 0)
            out << operande.getValeur()->getValeurInteger();
		else if(operande.getValeur()->getT()->getClass()
				.compare("real") == 0)
            out << operande.getValeur()->getValeurFloat();
		else if(operande.getValeur()->getT()->getClass()
				.compare("string") == 0)
            out << *(operande.getValeur()->getValeurString());
	} else {
		out << str;
	}
	return out.str();
}

string CodeInstruction::Cpy(TableId &ti, Operande& res, Operande& arg)
{
	string strRes = ti.getId(res.getNum());
	string strArg = ti.getId(arg.getNum());

	strRes = stringCheck(strRes, res);
	//cout << " ----------------------- " << endl;
	strArg = stringCheck(strArg, arg);

    return (strRes + " = " + strArg);
}

string CodeInstruction::Mul(TableId &ti, Operande& resultat,
		Operande& arg1, Operande& arg2)
{
	return opMath(ti, resultat, arg1, arg2, "*");
}

string CodeInstruction::Div(TableId &ti, Operande& resultat,
		Operande& arg1, Operande& arg2)
{
	return opMath(ti, resultat, arg1, arg2, "/");
}

string CodeInstruction::Mod(TableId &ti, Operande& resultat,
		Operande& arg1, Operande& arg2)
{
	return opMath(ti, resultat, arg1, arg2, "%");
}

string CodeInstruction::If(TableId &ti, Operande& res/*, Operande& arg1*/)
{
    string retour;

    retour = " si " + ti.getId(res.getNum()) + " aller a ";
    return retour;
}


string CodeInstruction::And(TableId &ti, Operande& res,
		Operande& arg1, Operande& arg2)
{
	return opBool(ti, res, arg1, arg2, "&&");
}

string CodeInstruction::Or(TableId &ti, Operande& res,
		Operande& arg1, Operande& arg2)
{
	return opBool(ti, res, arg1, arg2, "||");
}

string CodeInstruction::Xor(TableId &ti, Operande& res,
		Operande& arg1, Operande& arg2)
{
	return opBool(ti, res, arg1, arg2, "XOR");
}

string CodeInstruction::Not(TableId &ti, Operande& res, Operande& arg1)
{
	string retour;
	ostringstream out1;
	ostringstream out2;

    retour += ti.getId(res.getNum()) + " = ";

	if(arg1.EstCalculee())
	{
		if(arg1.getValeur()->getT()->getClass() == "boolean")
			out1 << arg1.getValeur()->getValeurBool();

		retour += out1.str();
		retour += + " ! ";
	}
	else
	{
		retour +=  " ! ";
		retour += ti.getId(arg1.getNum());
	}
	return retour;

}

string CodeInstruction::Eq(TableId &ti, Operande& res,
		Operande& arg1, Operande& arg2)
{
	return opMathStr(ti, res, arg1, arg2, "==");
}

string CodeInstruction::Neq(TableId &ti, Operande& res,
		Operande& arg1, Operande& arg2)
{
	return opMathStr(ti, res, arg1, arg2, "!=");
}

string CodeInstruction::Lt(TableId &ti, Operande& res,
		Operande& arg1, Operande& arg2)
{
	// Peut-on faire "test" < "toast" en Pascal :
	// La premiere version de ce fichier le permettait.
	// Erreur de copier-coller ?
	// Mettre opMathStr sinon.
	return opMath(ti, res, arg1, arg2, "<");
}

string CodeInstruction::Lte(TableId &ti, Operande& res,
		Operande& arg1, Operande& arg2)
{
	// Peut-on faire "test" < "toast" en Pascal :
	// La premiere version de ce fichier le permettait.
	// Erreur de copier-coller ?
	// Mettre opMathStr sinon.

	return opMath(ti, res, arg1, arg2, "<=");
}

string CodeInstruction::Gt(TableId &ti, Operande& res,
		Operande& arg1, Operande& arg2)
{
	// Peut-on faire "test" < "toast" en Pascal :
	// La premiere version de ce fichier le permettait.
	// Erreur de copier-coller ?
	// Mettre opMathStr sinon.

	return opMath(ti, res, arg1, arg2, ">");
}

string CodeInstruction::Gte(TableId &ti, Operande& res,
		Operande& arg1, Operande& arg2)
{
	// Peut-on faire "test" < "toast" en Pascal :
	// La premiere version de ce fichier le permettait.
	// Erreur de copier-coller ?
	// Mettre opMathStr sinon.

	return opMath(ti, res, arg1, arg2, ">");
}
